/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreem/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.renren.utils.util.db.connection;

import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.Hashtable;
import java.util.Properties;
import java.util.logging.Logger;

import javax.sql.DataSource;

import org.apache.commons.dbcp.ConnectionFactory;
import org.apache.commons.dbcp.DriverManagerConnectionFactory;
import org.apache.commons.dbcp.PoolableConnectionFactory;
import org.apache.commons.dbcp.PoolingDriver;
import org.apache.commons.pool.ObjectPool;
import org.apache.commons.pool.impl.GenericObjectPool;

/**
 * PoolingDataSource for Apache dbcp
 * 
 */
public class PoolingDataSource  implements DataSource {
	
	private String datasourceName;
	private org.apache.commons.dbcp.PoolingDataSource ds = null;
    private GenericObjectPool pool = null;
    private Properties configProperties = null;
        

	public PoolingDataSource(Properties configProperties) {
		this.configProperties = configProperties;
		try {
			setUp();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * @return Returns the datasourceName.
	 */
	protected String getDatasourceName() {
		return datasourceName;
	}

	public Connection getConnection() throws SQLException {
		Connection conn =  ds.getConnection();
		conn.setAutoCommit(false);
		return conn;
	}

	/* (non-Javadoc)
	 * @see javax.sql.DataSource#getConnection(java.lang.String, java.lang.String)
	 */
	public Connection getConnection(String arg0, String arg1) throws SQLException {
		return getConnection();
	}

	/* (non-Javadoc)
	 * @see javax.sql.DataSource#getLoginTimeout()
	 */
	public int getLoginTimeout() throws SQLException {
		// Auto-generated method stub
		return 0;
	}

	/* (non-Javadoc)
	 * @see javax.sql.DataSource#getLogWriter()
	 */
	public PrintWriter getLogWriter() throws SQLException {
		// Auto-generated method stub
		return null;
	}

	/* (non-Javadoc)
	 * @see javax.sql.DataSource#setLoginTimeout(int)
	 */
	public void setLoginTimeout(int arg0) throws SQLException {
		// Auto-generated method stub

	}

	/* (non-Javadoc)
	 * @see javax.sql.DataSource#setLogWriter(java.io.PrintWriter)
	 */
	public void setLogWriter(PrintWriter arg0) throws SQLException {
		// Auto-generated method stub

	}


	public Logger getParentLogger() throws SQLFeatureNotSupportedException {
		// TODO Auto-generated method stub
		return null;
	}

	public boolean isWrapperFor(Class<?> iface) throws SQLException {
		// TODO Auto-generated method stub
		return false;
	}

	public <T> T unwrap(Class<T> iface) throws SQLException {
		// TODO Auto-generated method stub
		return null;
	}

	    protected int getMaxActive() {
	    	if (configProperties!=null) {
	    		return Integer.parseInt(configProperties.getProperty(LocalDataSourceFactory.CONFIGDBCPMAXACTIVE, "10"));
	    	}
	        return 10;
	    }
	    
	    protected long getMaxWait() {
	    	if (configProperties!=null) {
	    		return Long.parseLong(configProperties.getProperty(LocalDataSourceFactory.CONFIGMAXWAIT, "100"));
	    	} 
	        return 100L;
	    }
	    
   


    private void setUp() throws Exception {

    	if (configProperties!=null) {
    		
    		datasourceName = configProperties.getProperty(LocalDataSourceFactory.CONFIGPREFIX);
    		
	        DriverManager.registerDriver((Driver) Class.forName(configProperties.getProperty(LocalDataSourceFactory.CONFIGDRIVERCLASSNAME)).newInstance());
	
	        pool = new GenericObjectPool();
	        pool.setMaxActive(getMaxActive());
	        pool.setMaxWait(getMaxWait());
	        
	        Properties props = new Properties();
	        props.setProperty("user", configProperties.getProperty(LocalDataSourceFactory.CONFIGUSERNAME));
	        props.setProperty("password", configProperties.getProperty(LocalDataSourceFactory.CONFIGPASSWORD));
	        
	        setupDriver(configProperties.getProperty(LocalDataSourceFactory.CONFIGURL), props, pool);
	        
	        ds = new org.apache.commons.dbcp.PoolingDataSource(pool);
	        ds.setAccessToUnderlyingConnectionAllowed(true);
    	} else {
    		throw new RuntimeException ("No datasource config propertied found! ");
    	}
    }

    private void tearDown() throws Exception {
        pool.close();
    }
    
    private void setupDriver(String connectURI,  Properties props, GenericObjectPool connectionPool) throws Exception {

        //
        // First, we'll create a ConnectionFactory that the
        // pool will use to create Connections.
        // We'll use the DriverManagerConnectionFactory,
        // using the connect string passed in the command line
        // arguments.
        //
        ConnectionFactory connectionFactory =
            new DriverManagerConnectionFactory(connectURI,props);

        //
        // Next, we'll create the PoolableConnectionFactory, which wraps
        // the "real" Connections created by the ConnectionFactory with
        // the classes that implement the pooling functionality.
        //

        //
        // Now we'll need a ObjectPool that serves as the
        // actual pool of connections.
        //
        // We'll use a GenericObjectPool instance, although
        // any ObjectPool implementation will suffice.
        //
        
        PoolableConnectionFactory poolableConnectionFactory =
                new PoolableConnectionFactory(connectionFactory, connectionPool,
                        null, null, false, true);
        
        connectionPool.setFactory(poolableConnectionFactory);
        //
        // Finally, we create the PoolingDriver itself...
        //
        Class.forName("org.apache.commons.dbcp.PoolingDriver");
        PoolingDriver driver = (PoolingDriver) DriverManager.getDriver("jdbc:apache:commons:dbcp:");

        //
        // ...and register our pool with it.
        //
        driver.registerPool(datasourceName,connectionPool);

        //
        // Now we can just use the connect string "jdbc:apache:commons:dbcp:example"
        // to access our pool of Connections.
        //
    }
    
    private static void printDriverStats() throws Exception {
        PoolingDriver driver = (PoolingDriver) DriverManager.getDriver("jdbc:apache:commons:dbcp:");
        ObjectPool connectionPool = driver.getConnectionPool("example");

        System.out.println("NumActive: " + connectionPool.getNumActive());
        System.out.println("NumIdle: " + connectionPool.getNumIdle());
    }

    private static void shutdownDriver() throws Exception {
        PoolingDriver driver = (PoolingDriver) DriverManager.getDriver("jdbc:apache:commons:dbcp:");
        driver.closePool("example");
    }
}

